/*
 *
 * Copyright (C) 2012 Walter Lütgenau
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 */
package offset.nodes.client.virtual.model.jcr.nodetype;

import java.io.OutputStream;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.jcr.RepositoryException;
import javax.jcr.ValueFormatException;
import offset.nodes.client.model.SimpleNamespaceRegistry;
import offset.nodes.client.model.XmlCharacterFilter;
import offset.nodes.client.virtual.model.jcr.PrintHandler;
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.AttributesImpl;

/**
 *
 * @author Walter Lütgenau
 */
public class NodeTypeWriter {

    ContentHandler handler;

    public NodeTypeWriter(ContentHandler handler) {
        this.handler = handler;
    }

    public static void write(OutputStream xml, NodeTypeDefinition[] types, SimpleNamespaceRegistry registry) throws Exception {
        NodeTypeWriter typeWriter = new NodeTypeWriter(new PrintHandler(xml));
        AttributesImpl atts = new AttributesImpl();
        String[] prefixes = SimpleNamespaceRegistry.getInstance().getPrefixes();
        for (int i = 0; i < prefixes.length; i++) {
            if (prefixes[i].length() > 0)
                typeWriter.addAttribute(atts, "xmlns:" + prefixes[i], SimpleNamespaceRegistry.getInstance().getURI(prefixes[i]));
        }
        typeWriter.startElement("nodeTypes", atts);
        for (int i = 0; i < types.length; i++) {
            typeWriter.writeNodeType(types[i]);
        }
        typeWriter.endElement("nodeTypes");
    }

    protected void endElement(String name) throws SAXException {
        handler.endElement("", "", name);
    }

    protected void startElement(String name, Attributes atts) throws SAXException {
        handler.startElement("", "", name, atts);
    }

    protected void startElement(String name) throws SAXException {
        startElement(name, null);
    }

    protected void addAttribute(AttributesImpl atts, String name, String value) throws SAXException {
        atts.addAttribute("", "", name, "CDATA", XmlCharacterFilter.filterAttributeValue(value));
    }

    protected void characters(String value) throws SAXException {
        handler.characters(value.toCharArray(), 0, value.length());
    }

    protected void writeNodeType(NodeTypeDefinition type) throws SAXException {
        AttributesImpl atts = new AttributesImpl();
        addAttribute(atts, "hasOrderableChildNodes", Boolean.toString(type.hasOrderableChildNodes()));
        addAttribute(atts, "isMixin", Boolean.toString(type.isMixin()));
        addAttribute(atts, "name", SimpleNamespaceRegistry.getInstance().toPrefixName(type.getName()));
        String primaryItemName = "";
        if (type.getPrimaryItemName() != null)
            primaryItemName = SimpleNamespaceRegistry.getInstance().toPrefixName(type.getPrimaryItemName());
        addAttribute(atts, "primaryItemName", primaryItemName);

        startElement("nodeType", atts);

        startElement("supertypes");
        for (int i = 0; i < type.getSupertypes().length; i++) {
            startElement("supertype");
            characters(SimpleNamespaceRegistry.getInstance().toPrefixName(type.getSupertypes()[i]));
            endElement("supertype");
        }
        endElement("supertypes");

        for (int i = 0; i < type.getDeclaredChildNodeDefinitions().length; i++) {
            writeChildNode(type.getDeclaredChildNodeDefinitions()[i]);
        }

        for (int i = 0; i < type.getDeclaredPropertyDefinitions().length; i++) {
            writeProperty(type.getDeclaredPropertyDefinitions()[i]);
        }

        endElement("nodeType");
    }

    protected void writeChildNode(NodeDefinition child) throws SAXException {
        AttributesImpl atts = new AttributesImpl();
        addAttribute(atts, "autoCreated", Boolean.toString(child.isAutoCreated()));
        addAttribute(atts, "mandatory", Boolean.toString(child.isMandatory()));
        addAttribute(atts, "name", SimpleNamespaceRegistry.getInstance().toPrefixName(child.getName()));
        addAttribute(atts, "onParentVersion", child.parentVersion(child.getOnParentVersion()));
        addAttribute(atts, "protected", Boolean.toString(child.isProtected()));
        addAttribute(atts, "sameNameSiblings", Boolean.toString(child.allowsSameNameSiblings()));
        addAttribute(atts, "defaultPrimaryType", SimpleNamespaceRegistry.getInstance().toPrefixName(child.getDefaultPrimaryType()));

        startElement("childNodeDefinition", atts);
        startElement("requiredPrimaryTypes");

        for (int i = 0; i < child.getRequiredPrimaryTypes().length; i++) {
            startElement("requiredPrimaryType");
            characters(SimpleNamespaceRegistry.getInstance().toPrefixName(child.getRequiredPrimaryTypes()[i]));
            endElement("requiredPrimaryType");
        }

        endElement("requiredPrimaryTypes");
        endElement("childNodeDefinition");

    }

    protected void writeProperty(PropertyDefinition property) throws SAXException {
        AttributesImpl atts = new AttributesImpl();
        addAttribute(atts, "autoCreated", Boolean.toString(property.isAutoCreated()));
        addAttribute(atts, "mandatory", Boolean.toString(property.isMandatory()));
        addAttribute(atts, "multiple", Boolean.toString(property.isMultiple()));
        addAttribute(atts, "name", SimpleNamespaceRegistry.getInstance().toPrefixName(property.getName()));
        addAttribute(atts, "onParentVersion", property.parentVersion(property.getOnParentVersion()));
        addAttribute(atts, "protected", Boolean.toString(property.isProtected()));
        addAttribute(atts, "requiredType", property.type(property.getRequiredType()));

        startElement("propertyDefinition", atts);
        
        if (property.getValueConstraints().length > 0) {
            startElement("valueConstraints");
            for (int i = 0; i < property.getValueConstraints().length; i++) {
                startElement("valueConstraint");
                characters(property.getValueConstraints()[i].getDefinition());
                endElement("valueConstraint");
            }
            endElement("valueConstraints");
        }

        if (property.getDefaultValues() != null && property.getDefaultValues().length > 0) {
            startElement("defaultValues");
            for (int i = 0; i < property.getDefaultValues().length; i++) {
                startElement("defaultValue");
                try {
                    characters(property.getDefaultValues()[i].getString());
                } catch (Exception ex) {
                    throw new SAXException(ex);
                }
                endElement("defaultValue");
            }
            endElement("defaultValues");
        }
        endElement("propertyDefinition");

    }
}
